// Copyright (C) 2010 nodchip

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License 2.0 for more details.

// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/

#include "JitRecompiler.h"

#include <llvm/Support/TypeBuilder.h>
#include <vector>
#include <cstdarg>

// It is capital for compiling that Dolphin's headers are included last.
//	They have a bunch of #define that interfere with the templated code
//	contained in LLVM's header files.
#include "../PowerPC.h"
#include "../PPCTables.h"
#include "../Interpreter/Interpreter.h"

namespace llvm
{
	template<bool xcompile>
	class TypeBuilder<PowerPC::PowerPCState, xcompile>
	{
	public:
		static const StructType* get(LLVMContext& context);
	};
}

namespace LLVMJit
{

llvm::FunctionType* JitRecompiler::CreateFunctionType(const llvm::Type* returnType, ...)
{
	std::vector<const llvm::Type*> types;
	va_list list;
	va_start(list, returnType);
	
	const llvm::Type* type;
	while ((type = va_arg(list, const llvm::Type*)) != NULL)
		types.push_back(type);
	va_end(list);
	
	return llvm::FunctionType::get(returnType, types, false);
}

void JitRecompiler::Default(UGeckoInstruction _inst, JitFunctionContext& functionContext)
{
	const u32 code = _inst.hex;
	llvm::IRBuilder<> builder = functionContext.GetBuilder();
	
	Interpreter::_interpreterInstruction function = GetInterpreterOp(code);
	
	llvm::Function* boundFunction = bindings->GetBinding((void*)function);
	if (boundFunction == NULL)
	{
		GekkoOPInfo* opcodeInfo = GetOpInfo(_inst);
		llvm::FunctionType* interpreterFunctionType = CreateFunctionType(
			llvm::Type::getVoidTy(context),
			llvm::Type::getInt32Ty(context), NULL);
		
		boundFunction = llvm::Function::Create(
			interpreterFunctionType,
			llvm::Function::ExternalLinkage,
			opcodeInfo->opname,
			module);
		
		bindings->Bind(boundFunction, (void*)function);
	}
	
	builder.CreateCall(boundFunction, builder.getInt32(code));
	
	// TODO how many cycles did that take?
	// functionContext.AddCycles(???);
}


enum {
	INDEX_MOJS,
	INDEX_GPR,
	INDEX_PS,
	INDEX_PC,
	INDEX_NPC,
	INDEX_CR,
	INDEX_CR_FAST,
	INDEX_MSR,
	INDEX_FPSCR,
	INDEX_EXCEPTIONS,
	INDEX_SR,
	INDEX_DEBUG_COUNT,
	INDEX_SPR,
	INDEX_DTLB_LAST,
	INDEX_DTLD_VA,
	INDEX_DTLB_PA,
	INDEX_ITLB_LAST,
	INDEX_ITLB_VA,
	INDEX_ITLB_PA,
};

llvm::Value* JitRecompiler::GetRegisterPointer(llvm::IRBuilder<>& builder, int reg)
{
	llvm::Value* pointer = GetPPCStatePointer(builder);
	return builder.CreateGEP(pointer, builder.getInt64(reg));
}

llvm::Value* JitRecompiler::GetRegisterPointer(llvm::IRBuilder<>& builder, int reg, int index)
{
	llvm::Value* value = GetRegisterPointer(builder, reg);
	return builder.CreateGEP(value, builder.getInt32(index));
}

llvm::Value* JitRecompiler::GetPPCStatePointer(llvm::IRBuilder<>& builder)
{
	intptr_t ppcStatePtr = (intptr_t)&PowerPC::ppcState;
	return builder.CreateIntToPtr(
		builder.getInt64(ppcStatePtr),
		llvm::TypeBuilder<PowerPC::PowerPCState, true>::get(context));
}

llvm::Value* JitRecompiler::GetGprPointer(llvm::IRBuilder<>& builder, int reg)
{
	return GetRegisterPointer(builder, INDEX_GPR, reg);
}

llvm::Value* JitRecompiler::GetSprPointer(llvm::IRBuilder<>& builder, int reg)
{
	return GetRegisterPointer(builder, INDEX_SPR, reg);
}

llvm::Value* JitRecompiler::GetCrPointer(llvm::IRBuilder<>& builder)
{
	return GetRegisterPointer(builder, INDEX_CR); 
}

llvm::Value* JitRecompiler::GetCrFastPointer(llvm::IRBuilder<>& builder, int reg)
{
	return GetRegisterPointer(builder, INDEX_CR_FAST, reg); 
}

llvm::Value* JitRecompiler::GetMsrPointer(llvm::IRBuilder<>& builder)
{
	return GetRegisterPointer(builder, INDEX_MSR);
}

llvm::Value* JitRecompiler::GetPsPointer(llvm::IRBuilder<>& builder, int reg)
{
	return GetRegisterPointer(builder, INDEX_PS, reg);
}

llvm::Value* JitRecompiler::GetFpscrPointer(llvm::IRBuilder<>& builder)
{
	return GetRegisterPointer(builder, INDEX_FPSCR);
}

llvm::Value* JitRecompiler::GetLinkPointer(llvm::IRBuilder<>& builder) 
{
	return GetSprPointer(builder, SPR_LR);
}

llvm::Value* JitRecompiler::GetCarryPointer(llvm::IRBuilder<>& builder)
{
	return GetSprPointer(builder, SPR_XER);
}

llvm::Value* JitRecompiler::GetCtrPointer(llvm::IRBuilder<>& builder)
{
	return GetSprPointer(builder, SPR_CTR);
}

llvm::Value* JitRecompiler::GetGqrPointer(llvm::IRBuilder<>& builder, int reg)
{
	return GetSprPointer(builder, SPR_GQR0 + reg);
}

llvm::Value* JitRecompiler::GetSrrPointer(llvm::IRBuilder<>& builder, int reg)
{
	return GetSprPointer(builder, SPR_SRR0 + reg);
}

llvm::Value* JitRecompiler::LoadGReg(llvm::IRBuilder<>& builder, int reg)
{
	char name[1024];
	sprintf(name, "greg%d", reg);
	return builder.CreateLoad(GetGprPointer(builder, reg), name);
}

llvm::Value* JitRecompiler::LoadFReg(llvm::IRBuilder<>& builder, int reg)
{
	char name[1024];
	sprintf(name, "freg%d", reg);
	return builder.CreateLoad(GetPsPointer(builder, reg), name);
}

llvm::Value* JitRecompiler::LoadLink(llvm::IRBuilder<>& builder)
{
	return builder.CreateLoad(GetLinkPointer(builder), "link");
}

llvm::Value* JitRecompiler::LoadCR(llvm::IRBuilder<>& builder, int reg)
{
	char name[1024];
	sprintf(name, "cr%d", reg);
	llvm::Value* byte = builder.CreateLoad(GetCrFastPointer(builder, reg), name);
	return builder.CreateTrunc(byte, builder.getInt1Ty());
}

llvm::Value* JitRecompiler::LoadCarry(llvm::IRBuilder<>& builder)
{
	llvm::Value* value = builder.CreateLoad(GetCarryPointer(builder), "carry");
	value = builder.CreateLShr(value, 29);
	return builder.CreateAnd(value, 1);
}

llvm::Value* JitRecompiler::LoadCTR(llvm::IRBuilder<>& builder)
{
	return builder.CreateLoad(GetCtrPointer(builder), "ctr");
}

llvm::Value* JitRecompiler::LoadMSR(llvm::IRBuilder<>& builder)
{
	return builder.CreateLoad(GetMsrPointer(builder), "msr");
}

llvm::Value* JitRecompiler::LoadGQR(llvm::IRBuilder<>& builder, int reg)
{
	char name[1024];
	sprintf(name, "gqr%d", reg);
	return builder.CreateLoad(GetGqrPointer(builder, reg), name);
}

llvm::Value* JitRecompiler::StoreGReg(llvm::IRBuilder<>& builder, int reg, llvm::Value* value)
{
	return builder.CreateStore(value, GetGprPointer(builder, reg));
}

llvm::Value* JitRecompiler::StoreFReg(llvm::IRBuilder<>& builder, int reg, llvm::Value* value)
{
	return builder.CreateStore(value, GetPsPointer(builder, reg));
}

llvm::Value* JitRecompiler::StoreLink(llvm::IRBuilder<>& builder, llvm::Value* value)
{
	return builder.CreateStore(value, GetLinkPointer(builder));
}

llvm::Value* JitRecompiler::StoreCR(llvm::IRBuilder<>& builder, int reg, llvm::Value* value)
{
	return builder.CreateStore(value, GetCrFastPointer(builder, reg));
}

llvm::Value* JitRecompiler::StoreCarry(llvm::IRBuilder<>& builder, llvm::Value* value)
{
	// TODO: Check whether the following code works because
	//       this implementation is different from Jit64/JitIL.
	value = builder.CreateICmpNE(value, builder.getInt32(0));			// value = value != 0
	value = builder.CreateZExt(value, llvm::Type::getInt32Ty(context));	// value = (int)value
	value = builder.CreateShl(value, 29);									// value <<= 29
	llvm::Value* carry = builder.CreateLoad(GetCarryPointer(builder), "carry");
	carry = builder.CreateAnd(carry, ~(1 << 29));							// carry &= ~(1 << 29)
	carry = builder.CreateOr(carry, value);								// carry |= value
	return builder.CreateStore(carry, GetCarryPointer(builder));
}

llvm::Value* JitRecompiler::StoreCTR(llvm::IRBuilder<>& builder, llvm::Value* value)
{
	return builder.CreateStore(value, GetCtrPointer(builder));
}

llvm::Value* JitRecompiler::StoreMSR(llvm::IRBuilder<>& builder, llvm::Value* value)
{
	return builder.CreateStore(value, GetMsrPointer(builder));
}

llvm::Value* JitRecompiler::StoreGQR(llvm::IRBuilder<>& builder, int reg, llvm::Value* value)
{
	return builder.CreateStore(value, GetGqrPointer(builder, reg));
}

llvm::Value* JitRecompiler::StoreFPRF(llvm::IRBuilder<>& builder, llvm::Value* value)
{
	value = builder.CreateAnd(value, 0x1F);
	value = builder.CreateShl(value, 12);
	llvm::Value* fpscr = builder.CreateLoad(GetFpscrPointer(builder));
	fpscr = builder.CreateAnd(fpscr, ~(0x1F << 12));
	fpscr = builder.CreateOr(fpscr, value);
	return builder.CreateStore(fpscr, GetFpscrPointer(builder));
}

llvm::Value* JitRecompiler::StoreSRR(llvm::IRBuilder<>& builder, int reg, llvm::Value* value)
{
	return builder.CreateStore(value, GetSrrPointer(builder, reg));
}

}
